package main

import (
	"fmt"
	"log"
	"net/http"
	"sort"
	"strconv"
	"strings"
)

type statistics struct {
	numbers []float64
	mean    float64
	median  float64
}

const form = `<html><body><form action="/" method="POST">
<label for="numbers">Numbers (comma or space-separated):</label><br>
<input type="text" name="numbers" size="30"><br />
<input type="submit" value="Calculate">
</form></html></body>`

const table = `<table border="1">
<tr><th colspan="2">Results</th></tr>
<tr><td>Numbers</td><td>%v</td></tr>
<tr><td>Count</td><td>%d</td></tr>
<tr><td>Mean</td><td>%f</td></tr>
<tr><td>Median</td><td>%f</td></tr>
</table>`

const errTag = `<p class="error">%s</p>`

var pageTop = ""
var pageBottom = ""

func main() {
	http.HandleFunc("/", homePage)
	if err := http.ListenAndServe(":9001", nil); err != nil {
		log.Fatal("Fail to start server:", err)
	}
}

func homePage(w http.ResponseWriter, r *http.Request) {
	// 请求头Content-Type
	w.Header().Set("Content-Type", "text/html")
	// 解析form
	err := r.ParseForm()
	// 输出表单
	fmt.Fprint(w, pageTop, form)
	if err != nil {
		fmt.Fprintf(w, errTag, err)
	} else {
		if numbers, msg, ok := handleReq(r); ok {
			stats := getStats(numbers)
			fmt.Fprintf(w, table, stats.numbers, len(numbers), stats.mean, stats.median)
		} else {
			fmt.Fprintf(w, errTag, msg)
		}
	}
	fmt.Fprint(w, pageBottom)
}

func handleReq(r *http.Request) ([]float64, string, bool) {
	var numbers []float64
	var txt string
	if slice, found := r.PostForm["numbers"]; found && len(slice) > 0 {
		// 中文逗号分隔
		if strings.Contains(slice[0], "&#65292") {
			txt = strings.Replace(slice[0], "&#65292", " ", -1)
		} else {
			txt = strings.Replace(slice[0], ",", " ", -1)
		}
		for _, str := range strings.Fields(txt) {
			if num, err := strconv.ParseFloat(str, 64); err != nil {
				return numbers, "'" + str + "' is invalid", false
			} else {
				numbers = append(numbers, num)
			}
		}
	}
	if len(numbers) == 0 {
		return numbers, "", false
	}
	return numbers, "", true
}

func getStats(nums []float64) (stats statistics) {
	stats.numbers = nums
	sort.Float64s(stats.numbers)
	stats.mean = sum(stats.numbers) / float64(len(nums))
	stats.median = median(stats.numbers)
	return
}

func sum(nums []float64) (res float64) {
	for _, num := range nums {
		res += num
	}
	return
}

func median(nums []float64) (res float64) {
	mid := len(nums) / 2
	res = nums[mid]

	if len(nums)%2 == 0 {
		res = (res + nums[mid-1]) / 2
	}
	return
}
